/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "XMChannel.h"
#include <math/utl.h>
#include "XMPlayer.h"

const int SAMPLE_PRECISION = 10;
const int SAMPLE_FRACMASK = 1023;

const int LOGFAC = 2*16;

template< typename T, bool interpolate >
static inline int get_sample( T * sample_data, int position )
{
	if( interpolate )
	{
		int sample = *(sample_data + (position >> SAMPLE_PRECISION));
		int next_sample = *(sample_data + (position >> SAMPLE_PRECISION) + 1);
		int frac = position & SAMPLE_FRACMASK;
		return sample + ((frac * (next_sample - sample))>>SAMPLE_PRECISION);
	}
	return *(sample_data + (position >> SAMPLE_PRECISION));
}

template< typename Dst, typename Src >
inline void write_sample( Dst * dst, int sample, int volume );

template<>
inline void write_sample<short int, short int>( short int * dst, int sample, int volume )
{
	*dst += (sample * volume) >> 10;
}
template<>
inline void write_sample<signed char, signed char>( signed char * dst, int sample, int volume )
{
	*dst += (sample * volume) >> 10;
}
template<>
inline void write_sample<short int, signed char>( short int * dst, int sample, int volume )
{
	*dst += (sample * volume) >> 2;
}
template<>
inline void write_sample<signed char, short int>( signed char * dst, int sample, int volume )
{
	*dst += (sample * volume) >> 18;
}

template< typename Dst, typename Src, bool Stereo >
void XMChannel::mix_samples( Dst * dst, Src * sample_data, int position, int samples_predicted )
{
	while( samples_predicted )
	{
		int sample = get_sample< Src, true >( sample_data, position );
		if( Stereo )
		{
				write_sample<Dst, Src>( dst++, sample, total_to_right );
				write_sample<Dst, Src>( dst++, sample, total_to_left );
		}
		else
			write_sample<Dst, Src>( dst++, sample, total_to_mono );

		--samples_predicted;
		position += increase;
	}
}

unsigned short logtab[] =
{
        LOGFAC*963,LOGFAC*956,LOGFAC*949,LOGFAC*942,LOGFAC*935,LOGFAC*928,LOGFAC*921,LOGFAC*914,
		LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887,LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862,
        LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838,LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814,
        LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791,LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768,
        LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746,LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725,
        LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704,LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684,
        LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665,LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646,
        LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628,LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610,
        LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592,LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575,
        LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559,LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543,
        LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528,LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513,
        LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498,LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484,
        LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470,LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457,
        LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443,LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431
};

/* Magic table for Fasttracker 2 linear frequency support */
static unsigned long gmpLinTable[768] =
{
        535232,534749,534266,533784,533303,532822,532341,531861,
        531381,530902,530423,529944,529466,528988,528511,528034,
        527558,527082,526607,526131,525657,525183,524709,524236,
        523763,523290,522818,522346,521875,521404,520934,520464,
        519994,519525,519057,518588,518121,517653,517186,516720,
        516253,515788,515322,514858,514393,513929,513465,513002,
        512539,512077,511615,511154,510692,510232,509771,509312,
        508852,508393,507934,507476,507018,506561,506104,505647,
        505191,504735,504280,503825,503371,502917,502463,502010,
        501557,501104,500652,500201,499749,499298,498848,498398,
        497948,497499,497050,496602,496154,495706,495259,494812,
        494366,493920,493474,493029,492585,492140,491696,491253,
        490809,490367,489924,489482,489041,488600,488159,487718,
        487278,486839,486400,485961,485522,485084,484647,484210,
        483773,483336,482900,482465,482029,481595,481160,480726,
        480292,479859,479426,478994,478562,478130,477699,477268,
        476837,476407,475977,475548,475119,474690,474262,473834,
        473407,472979,472553,472126,471701,471275,470850,470425,
        470001,469577,469153,468730,468307,467884,467462,467041,
        466619,466198,465778,465358,464938,464518,464099,463681,
        463262,462844,462427,462010,461593,461177,460760,460345,
        459930,459515,459100,458686,458272,457859,457446,457033,
        456621,456209,455797,455386,454975,454565,454155,453745,
        453336,452927,452518,452110,451702,451294,450887,450481,
        450074,449668,449262,448857,448452,448048,447644,447240,
        446836,446433,446030,445628,445226,444824,444423,444022,
        443622,443221,442821,442422,442023,441624,441226,440828,
        440430,440033,439636,439239,438843,438447,438051,437656,
        437261,436867,436473,436079,435686,435293,434900,434508,
        434116,433724,433333,432942,432551,432161,431771,431382,
        430992,430604,430215,429827,429439,429052,428665,428278,
        427892,427506,427120,426735,426350,425965,425581,425197,
        424813,424430,424047,423665,423283,422901,422519,422138,
        421757,421377,420997,420617,420237,419858,419479,419101,
        418723,418345,417968,417591,417214,416838,416462,416086,
        415711,415336,414961,414586,414212,413839,413465,413092,
        412720,412347,411975,411604,411232,410862,410491,410121,
        409751,409381,409012,408643,408274,407906,407538,407170,
        406803,406436,406069,405703,405337,404971,404606,404241,
        403876,403512,403148,402784,402421,402058,401695,401333,
        400970,400609,400247,399886,399525,399165,398805,398445,
        398086,397727,397368,397009,396651,396293,395936,395579,
        395222,394865,394509,394153,393798,393442,393087,392733,
        392378,392024,391671,391317,390964,390612,390259,389907,
        389556,389204,388853,388502,388152,387802,387452,387102,
        386753,386404,386056,385707,385359,385012,384664,384317,
        383971,383624,383278,382932,382587,382242,381897,381552,
        381208,380864,380521,380177,379834,379492,379149,378807,
        378466,378124,377783,377442,377102,376762,376422,376082,
        375743,375404,375065,374727,374389,374051,373714,373377,
        373040,372703,372367,372031,371695,371360,371025,370690,
        370356,370022,369688,369355,369021,368688,368356,368023,
        367691,367360,367028,366697,366366,366036,365706,365376,
        365046,364717,364388,364059,363731,363403,363075,362747,
        362420,362093,361766,361440,361114,360788,360463,360137,
        359813,359488,359164,358840,358516,358193,357869,357547,
        357224,356902,356580,356258,355937,355616,355295,354974,
        354654,354334,354014,353695,353376,353057,352739,352420,
        352103,351785,351468,351150,350834,350517,350201,349885,
        349569,349254,348939,348624,348310,347995,347682,347368,
        347055,346741,346429,346116,345804,345492,345180,344869,
        344558,344247,343936,343626,343316,343006,342697,342388,
        342079,341770,341462,341154,340846,340539,340231,339924,
        339618,339311,339005,338700,338394,338089,337784,337479,
        337175,336870,336566,336263,335959,335656,335354,335051,
        334749,334447,334145,333844,333542,333242,332941,332641,
        332341,332041,331741,331442,331143,330844,330546,330247,
        329950,329652,329355,329057,328761,328464,328168,327872,
        327576,327280,326985,326690,326395,326101,325807,325513,
        325219,324926,324633,324340,324047,323755,323463,323171,
        322879,322588,322297,322006,321716,321426,321136,320846,
        320557,320267,319978,319690,319401,319113,318825,318538,
        318250,317963,317676,317390,317103,316817,316532,316246,
        315961,315676,315391,315106,314822,314538,314254,313971,
        313688,313405,313122,312839,312557,312275,311994,311712,
        311431,311150,310869,310589,310309,310029,309749,309470,
        309190,308911,308633,308354,308076,307798,307521,307243,
        306966,306689,306412,306136,305860,305584,305308,305033,
        304758,304483,304208,303934,303659,303385,303112,302838,
        302565,302292,302019,301747,301475,301203,300931,300660,
        300388,300117,299847,299576,299306,299036,298766,298497,
        298227,297958,297689,297421,297153,296884,296617,296349,
        296082,295815,295548,295281,295015,294749,294483,294217,
        293952,293686,293421,293157,292892,292628,292364,292100,
        291837,291574,291311,291048,290785,290523,290261,289999,
        289737,289476,289215,288954,288693,288433,288173,287913,
        287653,287393,287134,286875,286616,286358,286099,285841,
        285583,285326,285068,284811,284554,284298,284041,283785,
        283529,283273,283017,282762,282507,282252,281998,281743,
        281489,281235,280981,280728,280475,280222,279969,279716,
        279464,279212,278960,278708,278457,278206,277955,277704,
        277453,277203,276953,276703,276453,276204,275955,275706,
        275457,275209,274960,274712,274465,274217,273970,273722,
        273476,273229,272982,272736,272490,272244,271999,271753,
        271508,271263,271018,270774,270530,270286,270042,269798,
        269555,269312,269069,268826,268583,268341,268099,267857 
};

// Sinus table
static int sinus_table[64] =
{
	0,25,25,37,49,60,71,81,90,98,106,112,117,122,125,126,
	127,126,125,122,117,112,106,98,90,81,71,60,49,37,25,12,
	0,-12,-25,-37,-49,-60,-71,-81,-90,-98,-106,-112,-117,-122,-125,-126,
	-127,-126,-125,-122,-117,-112,-106,-98,-90,-81,-71,-60,-49,-37,-25,-12
};

// Triangle wave table (ramp down)
static int ramp_down_table[64] =
{
	0,-4,-8,-12,-16,-20,-24,-28,-32,-36,-40,-44,-48,-52,-56,-60,
	-64,-68,-72,-76,-80,-84,-88,-92,-96,-100,-104,-108,-112,-116,-120,-124,
	127,123,119,115,111,107,103,99,95,91,87,83,79,75,71,67,
	63,59,55,51,47,43,39,35,31,27,23,19,15,11,7,3
};

// Square wave table
static int square_table[64] =
{
	127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,
	127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,
	-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,
	-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127
};

// Random wave table
static int random_table[64] =
{
	98,-127,-43,88,102,41,-65,-94,125,20,-71,-86,-70,-32,-16,-96,
	17,72,107,-5,116,-69,-62,-40,10,-61,65,109,-18,-38,-13,-76,
	-23,88,21,-94,8,106,21,-112,6,109,20,-88,-30,9,-127,118,
	42,-34,89,-4,-51,-72,21,-29,112,123,84,-101,-92,98,-54,-95
};

inline static int interpolate( int p, int p1, int p2, int v1, int v2 )
{
    if ( p1 == p2 )
		return v1;

    int dv = v2 - v1;
    int dp = p2 - p1;
    int di = p - p1;

    return v1 + di*dv/dp;
}

void XMChannel::set_key_off()
{
	key_off = true;
	volume_envelope.set_key_off();
	panning_envelope.set_key_off();
}

void XMChannel::reset()
{
	// Reset channels data
	old_position = position;
	old_period = period;
	volume_slide = 0;
	panning_slide = 0;
	global_volume_slide = 0;
	frequency_slide = 0;
	is_portamento = false;
	is_fine_vibrato = false;
	is_vibrato = false;
	is_tremolo = false;

	is_volume_slide = false;
	is_panning_slide = false;
	is_frequency_slide = false;

	count1 = 0;
	count2 = 0;
	count3 = 0;
	count4 = 0;
	count5 = 0;
}

void XMChannel::set_instrument_and_sample( int instrument_number, const XMInstrument::XMSample * sample, bool selected_old_instrument )
{
	old_instrument_number = instrument_number;
	increase = 0;
	position = 0;
	sample_data = 0;

	key_off = false;
	volume_envelope.reset();
	panning_envelope.reset();

	const XMInstrument * instrument = xm_player->get_instrument( instrument_number-1 );
	current_volume_fade = 65536;
	volume_fade = instrument->get_volume_fade();
	volume_envelope.associate( &instrument->get_volume_envelope() );
	panning_envelope.associate( &instrument->get_panning_envelope() );

	if( !sample->get_length() )
		return;

	sample_type = sample->type;
	sample_length = sample->sample_length << SAMPLE_PRECISION;
	sample_loop_start = sample->loop_start << SAMPLE_PRECISION;
	int sample_loop_end = (sample->loop_start + sample->loop_length) << SAMPLE_PRECISION;
	if( sample_loop_end )
		sample_length = sample_loop_end;
	sample_data = sample->data;

	if( !selected_old_instrument )
		volume = sample->volume;
	old_volume = volume;

	panning = sample->panning;
}

void XMChannel::update_note( const Note & note )
{
	int instrument_number = note.instrument;

	if( !xm_player->get_instrument( instrument_number-1 ) )
		instrument_number = 0;

	bool selected_old_instrument( false );
	if( !instrument_number )
	{
		selected_old_instrument = true;
		instrument_number = old_instrument_number;
	}

	unsigned new_period = 0;

//	bool was_fine_vibrato = is_fine_vibrato;
	bool was_vibrato = is_vibrato;
	bool was_tremolo = is_tremolo;

	int note_value = note.note;

	const XMInstrument::XMSample * sample = 0;

	if( note_value == 97 )
		set_key_off();
	else if( note_value )
	{
		const XMInstrument * instrument = xm_player->get_instrument( instrument_number-1 );
		if( instrument )
			sample = instrument->get_sample( note_value-1 );
		if( sample )
		{
			note_value += sample->relative_note;

			if( !xm_player->is_linear_table() )
			{
				// Amiga Freq
				int n = note_value%12;
				int o = note_value/12;

				int i = (n<<3) + (sample->fine_tune >> 4);

				int p1 = logtab[i+8];
				int p2 = logtab[i+1+8];

				new_period = ( interpolate( sample->fine_tune/16, 0, 15, p1, p2 ) >> o );
			}
			else
			{
				//Linear Freq
				new_period = ( 10*12*16*4 ) - (note_value*16*4) - (sample->fine_tune/2) + 64;
			}
		}
	}

	reset();

	if( new_period )
	{
		set_instrument_and_sample( instrument_number, sample, selected_old_instrument );

		period = new_period;
		portamento_dest = new_period;
	}
	else
		if( instrument_number )
		{
			period = old_period;
			if( !selected_old_instrument )
			{
				volume = 64;//old_volume;
				portamento_dest = old_period;
			}
		}

	int param = note.effect_parameter;

	//parse effect from volume column
	if( note.volume > 15 && note.volume < 16 + 0x41 )
	{
		volume = note.volume - 16;
		old_volume = note.volume - 16;
	}

	switch( note.effect )
	{
	// 00: Arpeggio
	case 0x00:
		if( !param || !period )
			break;
		count2 = 3;
		period2 = period;
		count1 = 2;
		period1 = period + ((param & 0x0F) << 4);
		period += ((param >> 4) & 0x0F) << 4;
		break;
	// 01: Portamento Up
	case 0x01:
		if( !param )
			param = old_frequency_slide;
		old_frequency_slide = param;
		frequency_slide = -(int)(param << 2);
		break;
	// 02: Portamento Down
	case 0x02:
		if( !param )
			param = old_frequency_slide;
		old_frequency_slide = param;
		frequency_slide = (param << 2);
		break;
	// 03: Tone-Portamento
	case 0x03:
		if( !param )
			param = portamento_slide >> 2;
		portamento_slide = param << 2;
		is_portamento = true;
		period = old_period;
		position = old_position;
		break;
	// 04: Vibrato
	case 0x04:
		if( !was_vibrato )
			vibrato_pos = 0;
		if( param )
			virato_slide = param;
		is_vibrato = true;
		break;
	// 05: Tone-Portamento + Volume Slide
	case 0x05:
		if( new_period )
		{
			portamento_dest = period;
			if( old_period )
				period = old_period;
		}
		is_portamento = true;
		if( param )
		{
			if( param & 0xF0 )
				volume_slide = int(param >> 4);// << 2);
			else
				volume_slide = -int(param & 0x0F);// << 2);
			old_volume_slide = volume_slide;
		}
		else
			volume_slide = old_volume_slide;
		break;
	// 06: Vibrato + Volume Slide
	case 0x06:
		if( !was_vibrato )
			vibrato_pos = 0;
		is_vibrato = true;
		if( param )
		{
			if( param & 0xF0 )
				volume_slide = int(param >> 4);// << 2);
			else
				volume_slide = -int(param & 0x0F);// << 2);
			old_volume_slide = volume_slide;
		}
		else
			volume_slide = old_volume_slide;
		break;
	// 0x07: Tremolo
	case 0x07:
		if( !was_tremolo )
			tremolo_pos = 0;
		if( param )
			tremolo_slide = param;
		is_tremolo = true;
		break;
	//0x08: Set Channel Pan Position
	case 0x08:
		panning = param;
		break;
	// 09: Set Offset
	case 0x09:
		if( param > 0 )
		{
			param <<= 8 + SAMPLE_PRECISION;
			if( param < sample_length )
				position = param;
		}
		break;
	// 0A: Volume Slide
	case 0x0A:
		if( param )
		{
			if( param & 0xF0 )
				volume_slide = int(param >> 4);// << 2);
			else
				volume_slide = -int(param & 0x0F);
			old_volume_slide = volume_slide;
		}
		else
			volume_slide = old_volume_slide;
	break;
	// 0B: Position Jump
	case 0x0B:
		xm_player->position_jump( param );
		break;
	//Set Volume
	case 0x0C:
		if( param > 0x40 )
			param = 0x40;
		volume = param;
		old_volume = param;
		break;
	// 0D: Pattern Break to row
	case 0x0D:
		if( param )
		{
            int hi=(param & 0xf0)>>4;
			int lo=(param & 0xf);
            int new_row = (hi*10)+lo - 1;

			xm_player->pattern_break_to_row( new_row );
		}
		else
			xm_player->set_row( 0xFFFF );
		break;
	// 0x0E: Extended Command
	case 0x0E:
		{
			int command = param >> 4;
			param &= 0x0F;
			switch( command )
			{
			// 0xE1: Fine Portamento Up
			case 0x01:
				if( period )
				{
					period -= param << 2;
					if( period < 1 )
						period = 1;
				}
				break;
			// 0xE2: Fine Portamento Down
			case 0x02:
				if( period )
					period += param << 2;
				break;
			//Glissando control - not implemented
			case 0x03:
				break;
			//Set Vibrato Waveform
			case 0x04:
				vibrato_type = param & 0x03;
				break;
			//Set FineTune - not implemented
			case 0x05:
				fine_tune = param;
				break;
			//Pattern Loop - not implemented
			case 0x06:
				break;
			//Set Tremolo Waveform
			case 0x07:
				tremolo_type = param & 0x03;
				break;
			// 0xE9: Retrig
			case 0x09:
				count4 = param + 1;
				count3 = param + 1;
				break;
			// 0xEA: Fine Volume Up
			case 0x0A:
				volume += param;// << 2;
				break;
			// 0xEB: Fine Volume Down
			case 0x0B:
				volume -= param;// << 2;
				break;
			//NotCut
			case 0x0C:
				count1 = param + 1;
				period1 = 0;
				break;
			//NoteDelay
			case 0x0D:
				count5 = param + 1;
				position = old_position;
				break;
			//PatternDelay
			case 0x0E:
				xm_player->set_pattern_delay( param );
				break;
			}
		}
		break;
	// 0F: Set Speed
	case 0x0F:
		if( param && param < 0x20 )
			xm_player->set_speed( param );
		else
			if( param >= 0x20 )
				xm_player->set_bpm( param );
		break;
	// 0G: Set Global volume
	case 0x10:
		if( param > 0x40 )
			param = 0x40;
		xm_player->set_global_volume( param );
		break;
	// 0H: Global volume slide
	case 0x11:
		if( param )
		{
			if( param & 0xF0 )
				global_volume_slide = int(param >> 4);// << 2);
			else
				global_volume_slide = -int(param & 0x0F);// << 2);
			old_global_volume_slide = global_volume_slide;
		}
		else
			global_volume_slide = old_global_volume_slide;
		break;
	// 0K: Key Off
	case 0x14:
		set_key_off();
		break;
	// 0L: Set Envelope position
	case 0x15:
		volume_envelope.set_position( param );
		panning_envelope.set_position( param );
		break;
	// 0P: Panning Slide
	case 0x19:
		if( param )
		{
			if( param & 0xF0 )
				panning_slide = int(param >> 4);// << 2);
			else
				panning_slide = -int(param & 0x0F);// << 2);
			old_panning_slide = panning_slide;
		}
		else
			panning_slide = old_panning_slide;
		break;
	// 0R: Multi Retrig Note
	case 0x1B:
		count4 = param & 0x0F + 1;
		count3 = param & 0x0F + 1;
		break;
	// 0T: Tremor
	case 0x1D:
		count2 = param & 0x0F + 1;
		period2 = period;
		count1 = (param >> 4) + 1;
		period1 = 0;
		break;
	// X1: Extra fine porta up - not implemented
	// X2: Extra fine porta down - not implemented
	}
}

void XMChannel::update_volume()
{
	current_volume = current_volume * xm_player->get_global_volume();

	total_to_left = current_volume * mix_to_left / 4096;
	total_to_right = current_volume * mix_to_right / 4096;

	total_to_mono = 128 * current_volume / 4096;
}

void XMChannel::update_tick()
{
	if( volume_slide )
		if( is_volume_slide )
			volume += volume_slide;
		else
			is_volume_slide = true;

	int global_volume = xm_player->get_global_volume();
	if( global_volume_slide )
		if( is_volume_slide )
		{
			global_volume += global_volume_slide;
			xm_player->set_global_volume( global_volume );
		}
		else
			is_volume_slide = true;

	global_volume = math::clamp( global_volume, 0, 64 );
	volume = math::clamp( volume, 0, 64 );

	if( panning_slide )
		if( is_panning_slide )
			panning += panning_slide;
		else
			is_panning_slide = true;

	panning = math::clamp( panning, 0, 255 );

	current_volume = volume;
	mix_to_left = panning;
	mix_to_right = 255 - mix_to_left;

	if( count1 )
	{
		--count1;
		if( !count1 )
			if( !period1 )
				volume = 0;
			else
			{
				period = period1;
				position = 0;

				volume_envelope.reset();
				panning_envelope.reset();
			}
	}
	if( count2 )
	{
		--count2;
		if( !count2 )
		{
			period = period2;
			position = 0;

			volume_envelope.reset();
			panning_envelope.reset();
		}
	}

	if( count4 )
	{
		--count4;
		if( !count4 )
		{
			position = 0;

			volume_envelope.reset();
			panning_envelope.reset();

			count4 = count3;
		}
	}

	if( count5 )
	{
		--count5;
		if( !count5 )
		{
			position = 0;

			volume_envelope.reset();
			panning_envelope.reset();
		}
		else
			return;
	}

	if( !period )
		return;

	if( is_tremolo )
	{
		switch( tremolo_type )
		{
		case 1:
			current_volume += ramp_down_table[ tremolo_pos ] * ( tremolo_slide & 0x0F ) / 127;
			break;
		case 2:
			current_volume += square_table[ tremolo_pos ] * ( tremolo_slide & 0x0F) / 127;
			break;
		case 3:
			current_volume += random_table[ tremolo_pos ] * ( tremolo_slide & 0x0F) / 127;
			break;
		default:
			current_volume += sinus_table[ tremolo_pos ] * ( tremolo_slide & 0x0F) / 127;
		}

		current_volume = math::clamp( current_volume, unsigned(0), unsigned(64) );
		volume = current_volume;
		tremolo_pos = ( tremolo_pos + ( tremolo_slide >> 4 ) ) & 0x3F;
	}
	if( is_portamento && portamento_dest )
	{
		if( period < portamento_dest )
		{
			period += portamento_slide;
			if( period > portamento_dest )
				period = portamento_dest;
		}
		if( period > portamento_dest )
		{
			period -= portamento_slide;
			if( period < portamento_dest )
				period = portamento_dest;
		}
	}

	if( frequency_slide )
		if( is_frequency_slide )
			period += frequency_slide;
		else
			is_frequency_slide = true;

	if( period < 1 )
		period = 1;

	if( is_vibrato )
	{
		switch( vibrato_type )
		{
		case 1:	
			period += ramp_down_table[ vibrato_pos ] * ( virato_slide & 0x0F) / 127;
			break;
		case 2:
			period += square_table[ vibrato_pos ] * ( virato_slide & 0x0F) / 127;
			break;
		case 3:
			period += random_table[ vibrato_pos ] * ( virato_slide & 0x0F) / 127;
			break;
		default:
			period += sinus_table[ vibrato_pos ] * ( virato_slide & 0x0F) / 127;
		}
		vibrato_pos = ( vibrato_pos + ( virato_slide >> 4 ) ) & 0x3F;
	}

	if( period < 1 )
		period = 1;

	if( key_off && !volume_envelope.is_active() )
	{
		sample_length = 0;
		sample_loop_start = 0;
		position = 0;
//		old_instrument_number = 0;
		increase = 0;
	}
	else
	{
		//envelope active
		if( volume_envelope.is_active() )
		{
			volume_envelope.advance();

			if( key_off )
			{
				current_volume_fade -= volume_fade;
				if ( current_volume_fade < 0 )
					current_volume_fade = 0;
			}

			short int envvol = volume_envelope.evaluate();

			current_volume *= envvol;
			current_volume /= 64;

			current_volume *= current_volume_fade;
			current_volume /= 65536;
		}

		//envelope active
		if( panning_envelope.is_active() && !is_panning_slide )
		{
			panning_envelope.advance();

			short int envpan = panning_envelope.evaluate();

			mix_to_left = mix_to_left + ( envpan - 32 )* 4;
			if( mix_to_left > 255 )
				mix_to_left = 255;
			if( mix_to_left < 0 )
				mix_to_left = 0;
			mix_to_right = 255 - mix_to_left;
		}
	}

	int temp_increase;

	if( !xm_player->is_linear_table() )
		temp_increase = ( ( 8363*1712 / period ) << SAMPLE_PRECISION ) / xm_player->get_format().get_frequency();
	else
		//Linear Freq Wicked! */
		temp_increase = ( ( gmpLinTable[ period % 768 ] >> ( period / 768) ) << SAMPLE_PRECISION ) / xm_player->get_format().get_frequency();

	if( increase < 0 )
		increase = -temp_increase;
	else
		increase = temp_increase;
}

void XMChannel::decode( void * where, int samples_count, const Format & format, bool interpolate )
{
	if( total_to_left == 0 && total_to_right == 0 )
		return;

	if( !sample_data || !sample_length || !increase )
		return;

	char * dst = (char *)where;
	while( samples_count && sample_length )
	{
		int samples_predicted = 0;
		if( increase < 0 )
			samples_predicted = (position - 1 - sample_loop_start )/-increase + 1;
		else
			samples_predicted = (sample_length - 1 - position)/increase + 1;
		if( samples_predicted > samples_count )
			samples_predicted = samples_count;

		samples_count -= samples_predicted;

		if( sample_type & 0x10 )
			if( format.get_bit_rate()== 16 )
				if( format.get_channels() == 2 )
					mix_samples<short int, short int, true>( (short int *)dst, (short int *)sample_data, position, samples_predicted );
				else
					mix_samples<short int, short int, false>( (short int *)dst, (short int *)sample_data, position, samples_predicted );
			else
				if( format.get_channels() == 2 )
					mix_samples<signed char, short int, true>( (signed char *)dst, (short int *)sample_data, position, samples_predicted );
				else
					mix_samples<signed char, short int, false>( (signed char *)dst, (short int *)sample_data, position, samples_predicted );
		else
			if( format.get_bit_rate() == 16 )
				if( format.get_channels() == 2 )
					mix_samples<short int, signed char, true>( (short int *)dst, (signed char *)sample_data, position, samples_predicted );
				else
					mix_samples<short int, signed char, false>( (short int *)dst, (signed char *)sample_data, position, samples_predicted );
			else
				if( format.get_channels() == 2 )
					mix_samples<signed char, signed char, true>( (signed char *)dst, (signed char *)sample_data, position, samples_predicted );
				else
					mix_samples<signed char, signed char, false>( (signed char *)dst, (signed char *)sample_data, position, samples_predicted );

		position += samples_predicted * increase;
		dst += samples_predicted * format.get_sample_size();

		//loop conditions
		if( position >= sample_length )
		{
			if( sample_type & 0x01 )
				position = (position & SAMPLE_FRACMASK) + sample_loop_start;
			else
				if( sample_type & 0x02 )
				{
					increase = -increase;
					position += increase;
				}
				else
					sample_length = 0;
		}
		else
			if( increase < 0 && position <= sample_loop_start )
			{
				increase = -increase;
				position += increase;
			}
	}
}

